<?php
declare(strict_types=1);

namespace App\Service;

use App\Model\Enum\RateTypeEnum;
use App\Model\Enum\StatusEnum;
use App\Utils\CustomUtils;
use Hyperf\Di\Annotation\Inject;
use App\Model\Dao\RateCustomizedDao;
use App\Model\Dao\RateGroupDao;
use App\Model\Dao\MchConfigDao;
use App\Exception\XZXValidationException;
use App\Constants\XZXResponseCode;
use App\Utils\ArrayUtils;
use Hyperf\Utils\ApplicationContext;
use Hyperf\Utils\Coroutine;
use App\Utils\DateUtils;
use App\Service\RateBocService;


class RateCustomizedService
{
    /**
     * @Inject()
     * @var RateCustomizedDao
     */
    private $RateCustomizedDao;

    /**
     * @Inject()
     * @var RateGroupDao
     */
    private $RateGroupDao;

    /**
     * @Inject()
     * @var RateBocService
     */
    private $RateBocService;    

    /**
     * @Inject()
     * @var MchConfigDao
     */
    private $MchConfigDao;

    public function get($param)
    {
        $result = $this->RateCustomizedDao->get($param['id']);
        if (!$result) {
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::DATA_NOT_FOUND));
        }
        if(isset($result['createTime'])){
            $result['createTime'] = DateUtils::getDateTime($result['createTime']);
        }
        $result['finalRate'] = $this->calFinalRate($result);

        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE,XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE),$result);
    }

    public function del($param)
    {
        $result = $this->RateCustomizedDao->get($param['id']);
        if (!$result) {
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::DATA_NOT_FOUND));
        }

        try {
            $result = $this->RateCustomizedDao->del($param['id'],false);
        } catch (\Throwable $th) {
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::DEL_FAIL));
        }
        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE,XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE),CustomUtils::xzxEmptyObject());
    }

    public function add($param)
    {
        // TODO: Implement add() method.
        //$data            =  [
        //    'name'       => $param['name'],
        //    'desciption' => $param['desciption'],
        //
        //];
        $data = $param;
        $data['id'] = CustomUtils::createUUID();
        unset($data['mchIdList']);
        unset($data['uid']);
        unset($data['finalRate']);
        unset($data['offset']);
        unset($data['typeList']);
        CustomUtils::xzxDebug($data);

        if(isset($param['type'])){
            // rateGroupId+sourceCurrency+targetCurrency+type 做搜索条件判断是否已经存在，如果存在则提示失败
            $query_data = array(
                "rateGroupId" => $param['rateGroupId'],
                "sourceCurrency" => $param['sourceCurrency'],
                "targetCurrency" => $param['targetCurrency'],
                "type" => $param['type'],
            );
            if($this->isRateRecordExist($query_data))
            {
                throw new XZXValidationException('添加失败，记录已经存在');
            }

            try {
                $this->RateCustomizedDao->insert($data);
            } catch (\Throwable $th) {
                CustomUtils::xzxDebug($th);
                throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::ADD_FAIL));
            }
        }

        if(isset($param['typeList'])){
            foreach($param['typeList'] as $k => $v){
                if($v > 4 || $v < 1){
                    // throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::ADD_FAIL));
                    continue;
                }
                $query_data = array(
                    "rateGroupId" => $param['rateGroupId'],
                    "sourceCurrency" => $param['sourceCurrency'],
                    "targetCurrency" => $param['targetCurrency'],
                    "type" => $v,
                );
                if($this->isRateRecordExist($query_data))
                {
                    // throw new XZXValidationException('添加失败，记录已经存在');
                    continue;
                }   
                $data['id'] = CustomUtils::createUUID();
                $data['type'] = $v;
                try {
                    $this->RateCustomizedDao->insert($data);
                } catch (\Throwable $th) {
                    CustomUtils::xzxDebug($th);
                    // throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::ADD_FAIL));
                    continue;
                }             
            }
        }
         
        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE,XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE),CustomUtils::xzxEmptyObject());
    }

    public function edit($param)
    {
        $info = $this->RateCustomizedDao->get($param['id']);
        if (!$info) {
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::DATA_NOT_FOUND));
        }

        $id = $param['id'];
        $data = array();
        // isset($param['name']) && $data['name'] = $param['name'];
        // isset($param['desciption']) && $data['desciption'] = $param['desciption'];

        foreach($param as $k => $v){
            if($k == "id"){
                continue;
            }
            if(isset($v)){
                $data[$k] = $v;
            }
        }
        unset($data['mchIdList']);
        unset($data['uid']);
        unset($data['finalRate']);
        unset($data['offset']);
        CustomUtils::xzxDebug($data);

        try {
            $this->RateCustomizedDao->update($id, $data);
        } catch (\Throwable $th) {
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::EDIT_FAIL));
        }
        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE,XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE),CustomUtils::xzxEmptyObject());
    }

    public function list(array $param)
    {

        $limit = $param['limit'];
        $page = $param['page'];

        $total = $this->RateCustomizedDao->getCount($param);
        $list = $this->RateCustomizedDao->getList($param, $page, $limit);

        // CustomUtils::xzxDebug($list);
        foreach ($list as $k => $v) {
            // CustomUtils::xzxDebug($v);

            $v['strOffset'] = '-';

            if ($v['offsetType'] == 1) {
                // 数值

                $v['strOffset'] = '';
                if ($v['offsetValue'] < 0) {
                }
                else if ($v['offsetValue'] > 0) {
                    $v['strOffset'] .= '+ ';
                }

                $v['strOffset'] .= $v['offsetValue'] / 1e4;
            }
            else if ($v['offsetType'] == 2) {
                // 百分比
                $v['strOffset'] = '';
                if ($v['offsetPercentage'] < 0) {
                }
                else if ($v['offsetPercentage'] > 0) {
                    $v['strOffset'] .= '+ ';
                }

                $v['strOffset'] .= $v['offsetPercentage'] / 1e2 . ' %';
            }

            $v['finalRate'] = $this->calFinalRate($v);

            if(isset($v['createTime'])){
                $v['createTime'] = DateUtils::getDateTime($v['createTime']);
            }
            $list[$k] = $v;
        }

        $data = array(
            'total' => $total,
            'list' => $list
        );

        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE,XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE),$data);
    }


    //计算最终使用的汇率
    public function calFinalRate($param)
    {
        $finalRate = 0;

        if($param['offsetType'] == 1)
        {
            $finalRate = $param['rate'] + $param['offsetValue'];
        }
        else
        {
            $finalRate = ceil($param['rate'] * ((10000 + $param['offsetPercentage']) /10000)); //取最大整数
        }
        return $finalRate;
    }

    private function isRateRecordExist($param)
    {
        $data = array(
            "rateGroupId" => $param['rateGroupId'],
            "sourceCurrency" => $param['sourceCurrency'],
            "targetCurrency" => $param['targetCurrency'],
            "type" => $param['type'],
        );
        $result = $this->RateCustomizedDao->getWhere($data);
        if(!$result)
        {
            return false;
        }
        return true;
    }

    public function getRateTypeOptions () {

        $list = RateCustomizedDao::RATE_TYPE;
        $options = ArrayUtils::ArrToOptions($list);

        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE,XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE),$options);
    }

    public function getTypeOptions () {
        $list = RateCustomizedDao::TYPE;
        $options = ArrayUtils::ArrToOptions($list);

        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE,XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE),$options);
    }

    public function getOffsetTypeOptions () {
        $list = RateCustomizedDao::OFFSET_TYPE;
        $options = ArrayUtils::ArrToOptions($list);

        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE,XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE),$options);
    }

    public function getSyncTypeOptions () {
        $list = RateCustomizedDao::SYNC_TYPE;
        $options = ArrayUtils::ArrToOptions($list);

        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE,XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE),$options);
    }

    public function getTransRateAndAmount($param){
        CustomUtils::xzxDebug($param);

        $queryParam = array(
            'mchId' => $param['mchId']
        );

        $mchInfo = $this->MchConfigDao->getWhere($queryParam);
        if(!$mchInfo){
            throw new XZXValidationException(XZXResponseCode::getMessage(XZXResponseCode::DATA_NOT_FOUND));
        }

        $mpSettleData = [
            'rateGroupId'    => $mchInfo['settleExchangeRateId'],
            'sourceCurrency' => $param['sourceCurrency'],
            'targetCurrency' => $param['targetCurrency'],
            // 'type'        => RateTypeEnum::SETTLE,
            'status'         => StatusEnum::ON,
            'transAmount'    => $param['amount'],
        ];
        if($param['type'] == 2){
            $mpSettleData['type'] = RateTypeEnum::TRANSFER;
        }

        if($param['type'] == 1){
            $mpSettleData['type'] = RateTypeEnum::WITHDRAW;
        }

        // 查汇率组是否有效
        $query_param = [
            'id'     => $mpSettleData['rateGroupId'],
            'status' => StatusEnum::ON,
        ];
        $group = $this->RateGroupDao->getWhere($query_param);

        if (!$group) {
            throw new XZXValidationException('未找到汇率组');
        }

        $mpSettleReturnData = array();
        // $mpSettleReturnData['platformCurrency'] = $mpSettleData['targetCurrency'];

        $where = [
            'rateGroupId'    => $mpSettleData['rateGroupId'],
            'sourceCurrency' => $mpSettleData['sourceCurrency'],
            'targetCurrency' => $mpSettleData['targetCurrency'],
            'type'           => $mpSettleData['type'],
            'status'         => StatusEnum::ON,
        ];
        $result = $this->RateCustomizedDao->getWhere($where);

        if (!$result) {
            // throw new Exception('未找到相关定制汇率记录', FAILED_CODE); // ToDo 这里要考虑是否以中行基本汇率为默认值给他送
            throw new XZXValidationException('未找到相关定制汇率记录');
        }

        $finalRate =  $this->calFinalRate($result);
        $mpSettleReturnData['transAmount'] = round(($finalRate / 10000) * $mpSettleData['transAmount']);
        $mpSettleReturnData['transRate'] = $finalRate;

        // $final_arr = array();
        // $final_arr['mchToPlatform'] = $mpSettleReturnData;
        // $final_arr['platformToBank'] = $pbSettleReturnData;

        CustomUtils::xzxDebug($mpSettleReturnData);

        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE,XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE),$mpSettleReturnData);
    }

    /**
     * @param $rateGroupId    string 汇率组ID
     * @param $sourceCurrency string 源币种
     * @param $targetCurrency string 目标币种
     * @param $rateType       RateTypeEnum 汇率类型
     * @param $amount
     *
     * @throws \Exception
     */
    protected function _getRateAndAmount ($rateGroupId, $sourceCurrency, $targetCurrency, RateTypeEnum $rateType, $amount) {

        $where = [
            'rateGroupId'    => $rateGroupId,
            'sourceCurrency' => $sourceCurrency,
            'targetCurrency' => $targetCurrency,
            'type'           => $rateType->value(),
            'status'         => StatusEnum::ON,
        ];
        $re = $this->RateCustomizedDao->getWhere($where);

        if (!$re) {
            throw new \Exception(sprintf('未找到 %s -> %s [%s] 汇率定制记录', $sourceCurrency, $targetCurrency, $rateType->label()));
        }

        $rate   = $this->calFinalRate($re);
        $amount = round(($rate / 10000) * $amount);

        return [
            'rate'   => $rate,
            'amount' => $amount,
        ];
    }

    /**
     *
     * 商户交易金额(mchTransAmount) = 用户支付的金额
     * 商户结算金额(settleAmount) = 商户交易金额 * 结算汇率(商户结算币种)
     * 通道交易金额(channelTransAmount) = 商户交易金额 * 交易汇率(用户交易币种)
     * 通道限额扣除(channelLimitAmount) = 商户交易金额 * 结算汇率(通道结算币种)
     *
     * @param $param
     *
     * @return array
     * @throws \Exception
     */
    public function getSettleRateAndAmount($param)
    {

        CustomUtils::xzxDebug($param);

        if (empty($param['channelList'])) {
            throw new XZXValidationException('通道列表为空');
        }

        // 汇率组检查
        $rateGroup = $this->RateGroupDao->getWhere(['id' => $param['rateGroupId'], 'status' => StatusEnum::ON]);
        if (!$rateGroup) {
            throw new XZXValidationException('未找到汇率组');
        }

        // 商户->平台
        $mpSettleReturnData = [];

        $rateAmount = $this->_getRateAndAmount($rateGroup['id'], $param['mchTransCurrency'], $param['mchSettleCurrency'], RateTypeEnum::born(RateTypeEnum::SETTLE), $param['mchTransAmount']);

        $mpSettleReturnData['platformCurrency']    = $param['mchSettleCurrency'];
        $mpSettleReturnData['platformTransRate']   = $rateAmount['rate'];
        $mpSettleReturnData['platformTransAmount'] = $rateAmount['amount'];

        // 平台->通道
        $pbSettleReturnData = [];

        foreach ($param['channelList'] as $channel) {

            $item = [
                'channelId'   => $channel['id'],
                'channelNo'   => $channel['channelNo'],
                'channelName' => $channel['channelName'],
                'sort'        => $channel['sort'],

                // 通道交易上送
                'channelTransCurrency' => $param['mchTransCurrency'],
                'channelTransAmount'   => 0,
                'channelTransRate'     => 0,

                // 通道结算限额
                'channelLimitCurrency' => $channel['settleCurrency'],
                'channelLimitAmount'   => 0,
                'channelLimitRate'     => 0,
            ];

            $rateAmountTrans = $this->_getRateAndAmount($rateGroup['id'], $param['mchTransCurrency'], $item['channelTransCurrency'], RateTypeEnum::born(RateTypeEnum::TRADE), $param['mchTransAmount']);
            $rateAmountLimit = $this->_getRateAndAmount($rateGroup['id'], $param['mchTransCurrency'], $item['channelLimitCurrency'], RateTypeEnum::born(RateTypeEnum::SETTLE), $param['mchTransAmount']);

            $item['channelTransAmount'] = $rateAmountTrans['amount'];
            $item['channelTransRate']   = $rateAmountTrans['rate'];
            $item['channelLimitAmount'] = $rateAmountLimit['amount'];
            $item['channelLimitRate']   = $rateAmountLimit['rate'];

            array_push($pbSettleReturnData, $item);
        }

        $data = [
            'mchToPlatform'  => $mpSettleReturnData,
            'platformToBank' => $pbSettleReturnData,
        ];

        CustomUtils::xzxDebug($data);

        return CustomUtils::buildSuccess($data);
    }

    public function getRefundAmount($param)
    {
        CustomUtils::xzxDebug($param);

        if($param['bankTransRate'] == 0)
        {
            throw new XZXValidationException('未找到相关费率');
        }

        $platformTransRate = $param['platformTransRate'];
        $bankTransRate = $param['bankTransRate'];
        $mchTransAmount = $param['mchTransAmount'];

        $mpSettleReturnData = array();
        $mpSettleReturnData['platformTransAmount'] = round(($platformTransRate / 10000) * $mchTransAmount);
        // $mpSettleReturnData['bankTransAmount'] = round((10000 / $bankTransRate ) * $mpSettleReturnData['platformTransAmount']);
        $mpSettleReturnData['bankTransAmount'] = round(($bankTransRate / 10000) * $mchTransAmount);

        return CustomUtils::xeServiceReturnData(XZXResponseCode::SUCCESS_CODE,XZXResponseCode::getMessage(XZXResponseCode::SUCCESS_CODE),$mpSettleReturnData);

    }

    /**
     * 从中行汇率表和自定义汇率表同步汇率
     * @param $param
     *
     * @return bool|void
     */
    public function syncRate (string $currencyCode) {

        // 读取受影响的汇率定制记录
        $srcList = $this->RateCustomizedDao->getWhereList([
            "sourceCurrency" => $currencyCode,
            "syncType" => 1,
        ]);
        $dstList = $this->RateCustomizedDao->getWhereList([
            "targetCurrency" => $currencyCode,
            "syncType" => 1,
        ]);

        $list = array_merge($srcList, $dstList);

        foreach ($list as $item) {
            try {
                $this->_handleSyncRate($item);
            } catch (\Exception $e) {
                CustomUtils::xzxDebug($e);
            }
        }

        return true;
    }

    /**
     * 处理每一条汇率定制的同步
     * @param $item
     *
     * @throws Exception
     */
    protected function _handleSyncRate ($item) {

        // 计算汇率
        $rate = $this->RateBocService->getRate([
            "type"         => $item["rateType"],
            "srcCurrency"  => $item["sourceCurrency"],
            "destCurrency" => $item["targetCurrency"],
        ])["data"]["rate"];

        if ($rate != $item["rate"]) {

            $update         = [];
            $update["rate"] = $rate;

            $this->RateCustomizedDao->set($item["id"], $update);
        }
    }
}
